

import Foundation
import MachO

@objcMembers class FYMemoryUtil: NSObject {

    //  MB-> 256 MB
    private static func fy_roundToNearest256MB(_ valueMB: Double) -> Double {
        let toNearest: Double = 256
        let remainder = Int(valueMB) % Int(toNearest)
        if remainder >= Int(toNearest / 2) {
            return Double((Int(valueMB) - remainder) + Int(toNearest))
        } else {
            return Double(Int(valueMB) - remainder)
        }
    }

    
    static func fy_getUsedMemorySize() -> Double {

        var totalUsedMemory: Double = 0.00
        let hostPort: mach_port_t = mach_host_self()
        var hostSize: mach_msg_type_number_t = UInt32(MemoryLayout<vm_statistics_data_t>.size / MemoryLayout<integer_t>.size)
        var pagesize: vm_size_t = 0
        
        host_page_size(hostPort, &pagesize)
        
        var vmStat = vm_statistics_data_t()
        let result = withUnsafeMutablePointer(to: &vmStat) {
            $0.withMemoryRebound(to: integer_t.self, capacity: Int(hostSize)) {
                host_statistics(hostPort, HOST_VM_INFO, $0, &hostSize)
            }
        }
        
        guard result == KERN_SUCCESS else {
            return -1
        }
        
        let usedPages = UInt(vmStat.active_count) +
                       UInt(vmStat.inactive_count) +
                       UInt(vmStat.wire_count)
        let usedMemory = Double(usedPages * UInt(pagesize))
        
        totalUsedMemory = (usedMemory / 1024.0) / 1024.0
        
        return totalUsedMemory > 0 ? totalUsedMemory : -1
    }
    
    // Byte
    static func fy_diskSpaceFree() -> Int64 {
        guard let attrs = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()),
              let space = attrs[.systemFreeSize] as? Int64 else {
            return -1
        }
        return space < 0 ? -1 : space
    }
    
    // Byte
    static func fy_diskSpace() -> Int64 {
        guard let attrs = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory()),
              let space = attrs[.systemSize] as? Int64 else {
            return -1
        }
        return space < 0 ? -1 : space
    }
    
    // GB
    static func fy_getRamAvailableSize() -> String {
        let total = Double(fy_getRamTotalMemory()) ?? 0
        let usedMB = fy_getUsedMemorySize()
        let available = total - usedMB / 1024.0
        return String(format: "%.6f", max(available, 0))
    }

    // GB
    static func fy_getRamTotalMemory() -> String {
        let physical = Double(ProcessInfo.processInfo.physicalMemory) / 1024.0 / 1024.0   // MB
        let rounded = FYMemoryUtil.fy_roundToNearest256MB(physical) / 1024.0                 // GB
        return String(format: "%.6f", max(rounded, 0))
    }

    // GB
    static func fy_getCashAvailableSize() -> String {
        let bytes = fy_diskSpaceFree()
        guard bytes >= 0 else { return "0" }
        let gb = Double(bytes) / 1024.0 / 1024.0 / 1024.0
        return String(format: "%.6f", gb)
    }
    
    // GB
    static func fy_getCashTotalSize() -> String {
        let bytes = fy_diskSpace()
        guard bytes >= 0 else { return "0" }
        let gb = Double(bytes) / 1024.0 / 1024.0 / 1024.0
        return String(format: "%.6f", gb)
    }
}

